---
title: Provider 对比 Riverpod
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import CodeBlock from "@theme/CodeBlock";
import family from "/docs/from_provider/family";
import {
  trimSnippet,
  AutoSnippet,
  When,
} from "@site/src/components/CodeSnippet";


<!---
This article recaps the defferences and the similarities between Provider and Riverpod.
-->
本文将介绍 Provider 和 Riverpod 之间的差异和相似之处。

<!---
## Defining providers
-->
## 定义提供者程序

<!---
The primary difference between both packages is how "providers" are defined.
-->
这两个包之间的主要区别在于如何定义“提供者程序”。

<!---
With [Provider], providers are widgets and as such placed inside the widget tree,
typically inside a `MultiProvider`:
-->
对于 [Provider]，提供者程序是小部件，因此放置在小部件树中，通常位于 `MultiProvider`：

```dart
class Counter extends ChangeNotifier {
 ...
}

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<Counter>(create: (context) => Counter()),
      ],
      child: MyApp(),
    )
  );
}
```

<!---
With Riverpod, providers are **not** widgets. Instead they are plain Dart objects.  
Similarly, providers are defined outside of the widget tree, and instead are declared
as global final variables.
-->
使用 Riverpod，提供者程序**不是**小部件。相反，它们是普通的 Dart 对象。  
同样，提供者程序在小部件树之外定义，而且声明为全局 final 变量。

<!---
Also, for Riverpod to work, it is necessary to add a `ProviderScope` widget above the
entire application. As such, the equivalent of the Provider example using Riverpod
would be:
-->
此外，要使 Riverpod 正常工作，必须在整个应用程序上方添加一个小 `ProviderScope` 部件。
因此，使用 Riverpod 和 Provider 示例等效的版本为：

```dart
// provider 现在是顶级变量
final counterProvider = ChangeNotifierProvider<Counter>((ref) => Counter());

void main() {
  runApp(
    // 该小部件为整个项目启用了 Riverpod
    ProviderScope(
      child: MyApp(),
    ),
  );
}
```

<!---
Notice how the provider definition simply moved up a few lines.
-->
请注意，这个 ChangeNotifierProvider 的定义只是向上移动了几行。

:::info
<!---
Since with Riverpod providers are plain Dart objects, it is possible to use
Riverpod without Flutter.  
For example, Riverpod can be used to write command line applications.
-->
由于 Riverpod 的提供者程序是普通的 Dart 对象，因此可以在没有 Flutter 的情况下使用 Riverpod。  
例如，Riverpod 可用于编写命令行应用程序。
:::

<!---
## Reading providers: BuildContext
-->
## 读取提供者程序：使用 BuildContext

<!---
With Provider, one way of reading providers is to use a Widget's `BuildContext`.
-->
使用 Provider 库，读取提供者程序的一种方法是使用 Widget 的 `BuildContext`。

<!---
For example, if a provider was defined as:
-->
例如，如果 provider 定义为：

```dart
Provider<Model>(...);
```

<!---
then reading it using [Provider] is done with:
-->
然后使用 [Provider] 读取它就可以这样做：

```dart
class Example extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Model model = context.watch<Model>();

  }
}
```

<!---
The equivalent in Riverpod would be:
-->
在 Riverpod 中的等效代码是：

```dart
final modelProvider = Provider<Model>(...);

class Example extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    Model model = ref.watch(modelProvider);

  }
}
```

<!---
Notice how:
-->
请注意：

<!---
- Riverpod's snippet extends `ConsumerWidget` instead of `StatelessWidget`.
  That different widget type adds one extra parameter to our `build` function:
  `WidgetRef`.
-->
- Riverpod 的代码片段是扩展 `ConsumerWidget` 的，而不是 `StatelessWidget`。
  不同的小部件类型为我们的 `build` 函数添加了一个额外的参数：`WidgetRef`。

<!---
- Instead of `BuildContext.watch`, in Riverpod we do `WidgetRef.watch`, using
  the `WidgetRef` which we obtained from `ConsumerWidget`.
-->
- 在 Riverpod 中我们使用 `WidgetRef.watch` 代替 `BuildContext.watch`，
  `WidgetRef` 是我们从 `ConsumerWidget` 拿到的。

<!---
- Riverpod does not rely on generic types. Instead it relies on the variable
  created using provider definition.
-->
- Riverpod 不依赖于泛型类型。相反，它依赖于使用提供者程序定义创建的变量。

<!---
Notice too how similar the wording is. Both Provider and Riverpod use the keyword
"watch" to describe "this widget should rebuild when the value changes".
-->
还要注意措辞的相似程度。Provider 和 Riverpod 都使用关键字“watch”来描述“当值更改时，这里的小部件应重新生成”。

:::info
<!---
Riverpod uses the same terminology as Provider for reading providers.
-->
Riverpod 使用与 Provider 相同的术语来读取提供者程序。

- `BuildContext.watch` -> `WidgetRef.watch`
- `BuildContext.read` -> `WidgetRef.read`
- `BuildContext.select` -> `WidgetRef.watch(myProvider.select)`

<!---
The rules for `context.watch` vs `context.read` applies to Riverpod too:  
Inside the `build` method, use "watch". Inside click handlers and other events,
use "read". When in need of filtering out values and rebuilds, use "select".
-->
`context.watch` 相对于 `context.read` 的规则也适用于 Riverpod：  
在 `build` 方法中，使用 “watch”。在单击处理程序和其他事件中，使用 “read”。
当需要过滤掉值并重新生成时，请使用 “select”。
:::

<!---
## Reading providers: Consumer
-->
## 读取提供者程序：使用 Consumer

<!---
Provider optionally comes with a widget named `Consumer` (and variants such as `Consumer2`)
for reading providers.
-->
Provider 可以选择附带一个名为 `Consumer`（以及名为 `Consumer2` 的变体）的小部件，用于读取提供者程序。

<!---
`Consumer` is helpful as a performance optimization, by allowing more granular rebuilds
of the widget tree - updating only the revelant widgets when the state changes:
-->
`Consumer` 作为性能优化很有帮助，它允许对小部件树进行更精细的重建 - 在状态更改时仅更新相关的小部件：

<!---
As such, if a provider was defined as:
-->
因此，如果一个 provider 被定义为：

```dart
Provider<Model>(...);
```

<!---
Provider allows reading that provider using `Consumer` with:
-->
Provider 允许使用 `Consumer` 读取这个 provider：

```dart
Consumer<Model>(
  builder: (BuildContext context, Model model, Widget? child) {

  }
)
```

<!---
Riverpod has the same principle. Riverpod, too, has a widget named `Consumer`
for the exact same purpose.
-->
Riverpod 也有同样的原理。Riverpod 也有一个以完全相同功能的 `Consumer` 小部件。

<!---
If we defined a provider as:
-->
如果我们将一个 provider 定义为：

```dart
final modelProvider = Provider<Model>(...);
```

<!---
Then using `Consumer` we could do:
-->
然后我们可以使用 `Consumer` 实现：

```dart
Consumer(
  builder: (BuildContext context, WidgetRef ref, Widget? child) {
    Model model = ref.watch(modelProvider);

  }
)
```

<!---
Notice how `Consumer` gives us a `WidgetRef` object. This is the same object
as we saw in the previous part related to `ConsumerWidget`.
-->
注意 `Consumer` 是如何给我们一个 `WidgetRef` 对象。这与我们在上一部分中看到的 `ConsumerWidget` 与相关的对象相同。

<!---
### There is no `ConsumerN` equivalent in Riverpod
-->
### Riverpod 中没有 `ConsumerN` 等效的类

<!---
Notice how pkg:Provider's `Consumer2`, `Consumer3` and such aren't needed nor missed in Riverpod.
-->
请注意，在 Riverpod 中不需要 pkg:Provider 的 `Consumer2`、`Consumer3` 等，也不要遗漏重构它们。

<!---
With Riverpod, if you want to read values from multiple providers, you can simply write multiple `ref.watch` statements,
like so:
-->
使用 Riverpod，如果要从多个提供者程序读取值，只需编写多个 ref.watch 语句即可，如下所示：

```dart
Consumer(
  builder: (context, ref, child) {
    Model1 model = ref.watch(model1Provider);
    Model2 model = ref.watch(model2Provider);
    Model3 model = ref.watch(model3Provider);
    // ...
  }
)
```

<!---
When compared to pkg:Provider's `ConsumerN` APIs, the above solution feels way less heavy and it should be easier to understand.
-->
与 pkg:Provider 的 `ConsumerN` API 相比，上述解决方案感觉不那么沉重，应该更容易理解。

<!---
## Combining providers: ProxyProvider with stateless objects
-->
## 组合提供者程序：ProxyProvider 与无状态对象

<!---
When using Provider, the official way of combining providers is using the
`ProxyProvider` widget (or variants such as `ProxyProvider2`).
-->
使用 Provider 时，组合提供者程序的官方方法是使用 `ProxyProvider` widget（或变体，例如 `ProxyProvider2`）。

<!---
For example we may define:
-->
例如，我们可以定义：

```dart
class UserIdNotifier extends ChangeNotifier {
  String? userId;
}

// ...

ChangeNotifierProvider<UserIdNotifier>(create: (context) => UserIdNotifier()),
```

<!---
From there we have two options. We may combine `UserIdNotifier` to create a new
"stateless" provider (typically an immutable value that possibly override ==).
Such as:
-->
在这里我们有两个选择。我们可以组合 `UserIdNotifier` 创建一个新的“无状态”提供者程序 
（通常是一个可能覆盖 == 的不可变值）。如：

```dart
ProxyProvider<UserIdNotifier, String>(
  update: (context, userIdNotifier, _) {
    return 'The user ID of the the user is ${userIdNotifier.userId}';
  }
)
```

<!---
This provider would automatically return a new `String` whenever
`UserIdNotifier.userId` changes.
-->
每当 `UserIdNotifier.userId` 发生更改时，这个提供者程序都会自动返回新的 `String` 值。

<!---
We can do something similar in Riverpod, but the syntax is different.  
First, in Riverpod, the definition of our `UserIdNotifier` would be:
-->
我们可以在 Riverpod 中做类似的事情，但语法不同。  
首先，在 Riverpod 中，我们的 `UserIdNotifier` 定义是：

```dart
class UserIdNotifier extends ChangeNotifier {
  String? userId;
}

// ...

final userIdNotifierProvider = ChangeNotifierProvider<UserIdNotifier>(
  (ref) => UserIdNotifier(),
);
```

<!---
From there, to generate our `String` based on the `userId`, we could do:
-->
那样的话，要基于 `userId` 生成 `String`，我们可以这样做：

```dart
final labelProvider = Provider<String>((ref) {
  UserIdNotifier userIdNotifier = ref.watch(userIdNotifierProvider);
  return 'The user ID of the the user is ${userIdNotifier.userId}';
});
```

<!---
Notice the line doing `ref.watch(userIdNotifierProvider)`.
-->
请注意这行 ref.watch(userIdNotifierProvider) 做的事。

<!---
This line of code tells Riverpod to obtain the content of the `userIdNotifierProvider`
and that whenever that value changes, `labelProvider` will be recomputed too.
As such, the `String` emitted by our `labelProvider` will automatically update
whenever the `userId` changes.
-->
这行代码告诉 Riverpod 获取 `userIdNotifierProvider` 的内容，
并且每当该值发生变化时， `labelProvider` 也会重新计算。
因此，每当 `userId` 更改时，
我们 `labelProvider` 发出的 `String` 都会自动更新。

<!---
This `ref.watch` line should feel similar. This pattern was covered previously
when explaining [how to read providers inside widgets](#reading-providers-buildcontext).
Indeed, providers are now able to listen to other providers in the same way
that widgets do.
-->
这行 `ref.watch` 应该感觉很熟悉。
之前在解释[如何在小部件中读取提供者程序](#读取-providersbuildcontext)时已经介绍了这个模式。
事实上，提供者程序现在能够与小部件以相同的方式监听其他提供者程序的改变。

<!---
## Combining providers: ProxyProvider with stateful objects
-->
## 组合提供者程序：ProxyProvider 与有状态对象

<!---
When combining providers, another alternative use-case is to expose
stateful objects, such as a `ChangeNotifier` instance.
-->
组合提供者程序时，另一个替代用例是公开有状态对象，例如 `ChangeNotifier` 实例。

<!---
For that, we could use `ChangeNotifierProxyProvider` (or variants such as `ChangeNotifierProxyProvider2`).  
For example we may define:
-->
为此，我们可以使用 `ChangeNotifierProxyProvider`（或变体，例如 `ChangeNotifierProxyProvider2`）。  
例如，我们可以定义：

```dart
class UserIdNotifier extends ChangeNotifier {
  String? userId;
}

// ...

ChangeNotifierProvider<UserIdNotifier>(create: (context) => UserIdNotifier()),
```

<!---
Then, we can define a new `ChangeNotifier` that is based on `UserIdNotifier.userId`.
For example we could do:
-->
然后，我们可以定义基于 `UserIdNotifier.userId` 的一个 `ChangeNotifier`。
例如，我们可以这样做：

```dart
class UserNotifier extends ChangeNotifier {
  String? _userId;

  void setUserId(String? userId) {
    if (userId != _userId) {
      print('The user ID changed from $_userId to $userId');
      _userId = userId;
    }
  }
}

// ...

ChangeNotifierProxyProvider<UserIdNotifier, UserNotifier>(
  create: (context) => UserNotifier(),
  update: (context, userIdNotifier, userNotifier) {
    return userNotifier!
      ..setUserId(userIdNotifier.userId);
  },
);
```

<!---
This new provider creates a single instance of `UserNotifier` (which is never re-constructed)
and prints a string whenever the user ID changes.
-->
这个新提供者程序会创建一个 `UserNotifier` 实例（它永远不会重新构造），
并在用户 ID 更改时打印一个字符串。

<!---
Doing the same thing in provider is achieved differently.
First, in Riverpod, the definition of our `UserIdNotifier` would be:
-->
在提供者程序中执行相同的操作是以不同的方式实现的。
首先，在 Riverpod 中，我们的 `UserIdNotifier` 是这样定义的：

```dart
class UserIdNotifier extends ChangeNotifier {
  String? userId;
}

// ...

final userIdNotifierProvider = ChangeNotifierProvider<UserIdNotifier>(
  (ref) => UserIdNotifier(),
),
```

<!---
From there, the equivalent to the previous `ChangeNotifierProxyProvider` would be:
-->
相比于上面 `ChangeNotifierProxyProvider` 的等价代码将是：

```dart
class UserNotifier extends ChangeNotifier {
  String? _userId;

  void setUserId(String? userId) {
    if (userId != _userId) {
      print('The user ID changed from $_userId to $userId');
      _userId = userId;
    }
  }
}

// ...

final userNotifierProvider = ChangeNotifierProvider<UserNotifier>((ref) {
  final userNotifier = UserNotifier();
  ref.listen<UserIdNotifier>(
    userIdNotifierProvider,
    (previous, next) {
      if (previous?.userId != next.userId) {
        userNotifier.setUserId(next.userId);
      }
    },
  );

  return userNotifier;
});
```

<!---
The core of this snippet is the `ref.listen` line.  
This `ref.listen` function is a utility that allows listening to a provider,
and whenever the provider changes, executes a function.
-->
这个片段的核心是 `ref.listen` 这行代码。  
这里的 `ref.listen` 函数是一个实用程序，它允许监听一个提供者程序，
并在提供者程序更改时执行函数。

<!---
The `previous` and `next` parameters of that function correspond to the
last value before the provider changed and the new value after it changed.
-->
该函数的 `previous` 和 `next` 参数对应于提供者程序更改前的最后一个值和更改后的新值。

<!---
## Scoping Providers vs `.family` + `.autoDispose`
-->
## 作用域提供者程序与 `.family` + `.autoDispose`

<!---
In pkg:Provider, scoping was used for two things:
  - destroying state when leaving a page
  - having custom state per page
-->
在 pkg:Provider 中，作用域用于两件事：
  - 离开页面时处置状态
  - 每页具有自定义状态

<!---
Using scoping just to destroy state isn't ideal.  
The problem is that scoping doesn't work well over large applications.  
For example, state often is created in one page, but destroyed later in a different page after navigation.  
This doesn't allow for multiple caches to be active over different pages.
-->
仅使用作用域来破坏状态并不理想。  
问题在于，作用域在大型应用程序上效果不佳。  
例如，状态通常在一个页面中创建，但在导航后稍后在另一个页面中处置。  
这不允许多个缓存在不同的页面上处于活动状态。

<!---
Similarly, the "custom state per page" approach quickly becomes difficult to handle if that state 
needs to be shared with another part of the widget tree, 
like you'd need with modals or a with a multi-step form.
-->
同样，如果需要与小部件树的另一部分共享状态，
“自定义每个页面状态”的方法很快就会变得难以处理，
就像你需要模态或多步骤表单一样。

<!---
Riverpod takes a different approach: first, scoping providers is kind-of discouraged; second, 
`.family` and `.autoDispose` are a complete replacement solution for this.
-->
Riverpod 采取了不同的方法：首先，不鼓励使用作用域提供者；
其次， `.family` 和 `.autoDispose` 是完整的替代解决方案。

<!---
Within Riverpod, Providers marked as `.autoDispose` automatically destroy their state when they aren't used anymore.  
When the last widget removing a provider is unmounted, Riverpod will detect this and destroy the provider.  
Try using these two lifecycle methods in a provider to test this behavior:
-->
在 Riverpod 中，当一个提供者程序标记为 `.autoDispose` 在不再使用时会自动处置的状态。  
当卸载最后一个删除提供者程序的小部件时，Riverpod 将检测到卸载并处置提供者程序。  
尝试在提供者程序中使用以下两种生命周期方法来测试此行为：

```dart
ref.onCancel((){
  print("我一个监听程序都没有了！");
});
ref.onDispose((){
  print("如果我已经被定义为 `.autoDispose`，我将被处置！");
});
```

<!---
This inherently solves the "destroying state" problem.
-->
这从本质上解决了“破坏状态”问题。

<!---
Also it is possible to mark a Provider as `.family` (and, at the same time, as `.autoDispose`).  
This enables passing parameters to providers, which make multiple providers to be spawned and tracked internally.  
In other words, when passing parameters, *a unique state is created per unique parameter*.
-->
此外，还可以将提供者程序标记为 `.family`（同时，也可以标记为 `.autoDispose`）。  
这样就可以将参数传递给提供者程序，从而在内部生成和跟踪多个提供者程序。  
换句话说，在传递参数时，*会为每个唯一参数创建一个唯一状态*。

<AutoSnippet language="dart" {...family}></AutoSnippet>


<!---
This solves the "custom state per page" problem. Actually, there's another advantage: such state is no-longer bound to one specific page.  
Instead, if a different page tries to access the same state, such page will be able to do so by just reusing the parameters.
-->
这解决了“每页自定义状态”问题。实际上，还有另一个优点：这种状态不再绑定到一个特定的页面。  
相反，如果不同的页面尝试访问相同的状态，则该页面只需重用参数即可实现。

<!---
In many ways, passing parameters to providers is equivalent to a Map key.  
If the key is the same, the value obtained is the same. If it's a different key, a different state will be obtained.
-->
在许多方面，将参数传递给提供者程序等同于 Map 的键。  
如果键相同，则获取的值相同。如果是不同的键，则将获得不同的状态。

[provider]: https://pub.dev/packages/provider
[ref.watch]: /docs/concepts/reading#using-refwatch-to-observe-a-provider
[ref.listen]: /docs/concepts/reading#using-reflisten-to-react-to-a-provider-change
[autodispose]: /docs/concepts2/auto_dispose
[workaround]: https://pub.dev/packages/provider#can-i-obtain-two-different-providers-using-the-same-type
[.family modifier]: /docs/concepts/modifiers/family
[keepAlive]: https://pub.dev/documentation/hooks_riverpod/latest/hooks_riverpod/Ref/keepAlive.html
[as these two features go hand-in-hand]: /docs/concepts/modifiers/family#prefer-using-autodispose-when-the-parameter-is-not-constant
[simplification of logic]: /docs/concepts/modifiers/family#usage
[we have to]: https://github.com/flutter/flutter/issues/128432
[it turns out]: https://github.com/flutter/flutter/issues/106549
[*can't* react when a consumer stops listening to them]: https://github.com/flutter/flutter/issues/106546
[integrates well with Flutter]: /docs/concepts/reading#using-reflisten-to-react-to-a-provider-change
[ChangeNotifierProvider]: https://pub.dev/documentation/hooks_riverpod/latest/legacy/ChangeNotifierProvider-class.html
[Code generation]: /docs/about_code_generation
[AsyncNotifiers]: https://pub.dev/documentation/hooks_riverpod/latest/hooks_riverpod/Notifier-class.html
[combining Providers]: /docs/concepts/combining_providers
[global final variable]: /docs/concepts2/providers#creating-a-provider
